package br.com.harley.torneio.business;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;

import br.com.harley.torneio.constant.MensagemAplicacaoErro;
import br.com.harley.torneio.domain.Jogador;
import br.com.harley.torneio.exception.NegocioException;
import br.com.harley.torneio.util.Constantes;
import br.com.harley.torneio.util.MensagemUtil;
import br.com.harley.torneio.validator.ValidadorGenerico;
import br.gov.frameworkdemoiselle.annotation.Name;
import br.gov.frameworkdemoiselle.message.DefaultMessage;
import br.gov.frameworkdemoiselle.message.Message;
import br.gov.frameworkdemoiselle.stereotype.BusinessController;
import br.gov.frameworkdemoiselle.transaction.Transactional;
import br.gov.frameworkdemoiselle.util.ResourceBundle;

@Name("jogadorBC")
@BusinessController
public class ManterJogadorBC extends ManterUsuarioBC {

	private static final long serialVersionUID = 1L;

	/**
	 * {@link Logger} da classe.
	 */
	@SuppressWarnings("unused")
	@Inject
	private Logger logger;

	/**
	 * Inclui um novo jogador.
	 * 
	 * @param novoJogador
	 *            Novo jogador sendo incluído.
	 * @throws NegocioException
	 *             Caso haja erros na validação é lançada exceção de negócio com
	 *             sua lista de mensagens preenchida.
	 */
	@Transactional
	public void incluirJogador(final Jogador novoJogador) throws NegocioException {
		validarJogador(novoJogador);
		getDelegate().insert(novoJogador);
	}

	/**
	 * Atualiza um jogador existente.
	 * 
	 * @param jogador
	 *            Jogador sendo atualizado.
	 * @throws NegocioException
	 *             Caso haja erros na validação é lançada exceção de negócio com
	 *             sua lista de mensagens preenchida.
	 */
	@Transactional
	public void atualizarJogador(final Jogador jogador) throws NegocioException {
		validarJogador(jogador);
		getDelegate().update(jogador);
	}

	/**
	 * Realiza todas as validações necessárias para atualizar ou incluir um novo
	 * jogador.
	 * 
	 * @param jogador
	 *            Jogador sendo incluído ou atualizado.
	 * @throws NegocioException
	 *             Caso haja erros na validação é lançada exceção de negócio com
	 *             sua lista de mensagens preenchida.
	 */
	private void validarJogador(Jogador jogador) throws NegocioException {
		List<Message> mensagens = new ArrayList<Message>();
		mensagens.addAll(validarObrigatoriedadeDosCampos(jogador));
		mensagens.addAll(validarFormacaoDosCampos(jogador));
		mensagens.addAll(validarConsistenciaDosCampos(jogador));

		if (!mensagens.isEmpty()) {
			throw new NegocioException("Falha na validação do jogador.", mensagens);
		}
	}

	/**
	 * Valida o preenchimento dos campos obrigatórios.
	 * 
	 * @param jogador
	 *            Jogador sendo validado.
	 * @return Lista contendo mensagens de erro de validação.
	 */
	private List<Message> validarObrigatoriedadeDosCampos(Jogador jogador) {
		List<Message> msgs = new ArrayList<Message>();
		msgs.addAll(super.validarObrigatoriedadeDosCampos(jogador));
		validadorGenerico.verificarPreenchimentoDoCampo(Constantes.LABELS.LABEL_APELIDO, jogador.getApelido(), msgs);
		return msgs;
	}

	/**
	 * Valida a formação dos campos.
	 * 
	 * @param jogador
	 *            Jogador sendo validado.
	 * @return Lista contendo mensagens de erro de validação.
	 */
	private List<Message> validarFormacaoDosCampos(Jogador jogador) {
		List<Message> msgs = new ArrayList<Message>();
		msgs.addAll(super.validarFormacaoDosCampos(jogador));
		validadorGenerico.validarTamanho(Constantes.LABELS.LABEL_APELIDO, jogador.getApelido(),
				Constantes.TAMANHOS.TAM_MIN_APELIDO, Constantes.TAMANHOS.TAM_MAX_APELIDO, msgs);
		return msgs;
	}

	/**
	 * Valida a consistência dos campos.
	 * 
	 * @param jogador
	 *            Jogador sendo validado.
	 * @return Lista contendo mensagens de erro de validação.
	 */
	protected List<Message> validarConsistenciaDosCampos(Jogador jogador) {
		List<Message> msgs = new ArrayList<Message>();

		if (jogador.getLogin() != null && existeUsuarioComMesmoLogin(jogador)) {
			Message msg = new DefaultMessage(mensagemUtil.buscaMensagem(MensagemAplicacaoErro.CAMPO_UNICO_DUPLICADO)
					.getText(), bundle.getString(Constantes.LABELS.LABEL_JOGADOR),
					bundle.getString(Constantes.LABELS.LABEL_LOGIN));
			msgs.add(msg);
		}
		if (jogador.getCpf() != null && existeUsuarioComMesmoCpf(jogador)) {
			Message msg = new DefaultMessage(mensagemUtil.buscaMensagem(MensagemAplicacaoErro.CAMPO_UNICO_DUPLICADO)
					.getText(), bundle.getString(Constantes.LABELS.LABEL_JOGADOR),
					bundle.getString(Constantes.LABELS.LABEL_CPF));
			msgs.add(msg);
		}

		if (jogador.getApelido() != null && existeJogadorComMesmoApelido(jogador)) {
			Message msg = new DefaultMessage(mensagemUtil.buscaMensagem(MensagemAplicacaoErro.CAMPO_UNICO_DUPLICADO)
					.getText(), bundle.getString(Constantes.LABELS.LABEL_JOGADOR),
					bundle.getString(Constantes.LABELS.LABEL_APELIDO));
			msgs.add(msg);
		}

		if (!senhaEhIgualConfirmacao(jogador)) {
			Message msg = new DefaultMessage(mensagemUtil.buscaMensagem(
					MensagemAplicacaoErro.SENHA_DIFERENTE_CONFIRMACAO).getText());
			msgs.add(msg);
		}

		return msgs;
	}

	private boolean existeJogadorComMesmoApelido(Jogador jogador) {
		return existeEntidadeComMesmoAtributo(jogador, "apelido", jogador.getApelido());
	}

	/**
	 * Verifica se já existe um jogador cadastrado com o login informado.
	 * 
	 * @param jogador
	 *            Jogador cujo login está sendo verificado.
	 * @param listaDeJogadoresCadastrados
	 *            Lista de jogadores cadastrados.
	 * @return True caso já exista jogador cadastrado com o login informado e
	 *         False em caso contrário.
	 */
	private boolean existeOutroJogadorComMesmoLogin(Jogador jogador, List<Jogador> listaDeJogadoresCadastrados) {
		for (Jogador jogadorCadastrado : listaDeJogadoresCadastrados) {
			if (jogador.getLogin() != null
					&& StringUtils.equalsIgnoreCase(jogadorCadastrado.getLogin().trim(), jogador.getLogin().trim())) {
				if (jogador.getId() == null || !jogadorCadastrado.getId().equals(jogador.getId())) {
					// retorna true apenas se existe jogador com mesmo login na
					// lista e este jogador não é ele mesmo
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Verifica se já existe um jogador cadastrado com o apelido informado.
	 * 
	 * @param jogador
	 *            Jogador cujo apelido está sendo verificado.
	 * @param listaDeJogadoresCadastrados
	 *            Lista de jogadores cadastrados.
	 * @return True caso já exista jogador cadastrado com o apelido informado e
	 *         False em caso contrário.
	 */
	private boolean existeOutroJogadorComMesmoApelido(Jogador jogador, List<Jogador> listaDeJogadoresCadastrados) {
		for (Jogador jogadorCadastrado : listaDeJogadoresCadastrados) {
			if (jogador.getApelido() != null
					&& StringUtils.equalsIgnoreCase(jogadorCadastrado.getApelido().trim(), jogador.getApelido().trim())) {
				if (jogador.getId() == null || !jogadorCadastrado.getId().equals(jogador.getId())) {
					// retorna true apenas se existe jogador com mesmo apelido
					// na
					// lista e este jogador não é ele mesmo
					return true;
				}
			}
		}
		return false;
	}

	public void setMensagemUtil(MensagemUtil mensagemUtil) {
		this.mensagemUtil = mensagemUtil;
	}

	public void setBundle(ResourceBundle bundle) {
		this.bundle = bundle;
	}

	/**
	 * {@inheritDoc}
	 */
	public void setLogger(Logger logger) {
		this.logger = logger;
	}

	/**
	 * @param validadorGenerico
	 *            the validadorGenerico to set
	 */
	public void setValidadorGenerico(ValidadorGenerico validadorGenerico) {
		this.validadorGenerico = validadorGenerico;
	}

}